package com.listeningframework.boot.codegen.context;

import com.listeningframework.boot.codegen.CodeGenEntity;
import com.listeningframework.boot.codegen.CodeGenField;
import com.listeningframework.boot.codegen.DatabasePlatform;
import com.listeningframework.boot.codegen.database.MetaLoader;
import com.listeningframework.boot.codegen.database.MysqlLoader;
import com.listeningframework.boot.codegen.exception.BusinessException;
import com.listeningframework.boot.codegen.lang.CodeGenStringUtils;
import com.listeningframework.boot.codegen.lang.ReflectUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;

/**
 * @author lixining
 * @version $Id: EntityConfig.java, v 0.1 2016年12月2日 下午7:15:20 lixining Exp $
 * @description CodeGen Config
 */
public class MetaContext {
    /**
     * 带有CodeGenEntity注解的类Class
     */
    private Class<?> clz;
    /**
     * CodeGenEntity注解參數
     */
    private CodeGenEntity codeGenEntity;
    /**
     * CodeGen Entity Meta List
     */
    private List<MetaData> entityMetaList;

    /**
     * 构造方法 - （Class<?>）
     *
     * @param clz - 帶有CodeGenEntity註解的類
     * @throws BusinessException
     */
    public MetaContext(Class<?> clz) throws BusinessException {
        this.clz = clz;
        this.codeGenEntity = clz.getAnnotation(CodeGenEntity.class);
        initLoader();
    }

    /**
     * Entity前缀
     */
    public String getModelPrefix() {
        return CodeGenStringUtils.capitalize(codeGenEntity.prefix());
    }

    /**
     * Entity后缀
     */
    public String getModelSuffix() {
        return CodeGenStringUtils.capitalize(codeGenEntity.suffix());
    }

    /**
     * CodeGenEntity
     */
    public CodeGenEntity getCodeGenEntity() {
        return codeGenEntity;
    }

    /**
     * 表名称
     */
    public String getTableName() {
        return getCodeGenEntity().table().toUpperCase();
    }

    /**
     * 模块功能描述
     */
    public String getModelDesc() {
        return codeGenEntity.desc();
    }

    /***************************
     * Controller
     ********************************/
    public String getControllerPackage() {
        return relatedPath(getEntityPackage(), ".web");
    }

    public String getControllerClass() {
        return getClzSimpleName(getControllerPackage(), "Controller");
    }

    public String getControllerPath() {
        return getJavaPath(getControllerClass());
    }

    /***************************
     * Service
     **********************************/
    public String getServicePackage() {
        return relatedPath(getEntityPackage(), ".service");
    }

    public String getServiceClass() {
        return getClzSimpleName(getServicePackage(), "Service");
    }

    public String getServicePath() {
        return getJavaPath(getServiceClass());
    }

    /***************************
     * Entity
     **********************************/
    public String getEntityPackage() {
        return ReflectUtils.getClassPackage(clz);
    }

    public Class<?> getClz() {
        return clz;
    }

    public String getEntityClass() {
        return clz.getName();
    }

    public String getEntityPath() {
        return getJavaPath(getEntityClass());
    }

    /***************************
     * Domain
     **********************************/
    public String getDomainPackage() {
        return getEntityPackage().replace(".entity", ".domain");
    }

    public String getDomainClass() {
        return getClzSimpleName(getDomainPackage(), "Model");
    }

    public String getDomainPath() {
        return getJavaPath(getDomainClass());
    }

    /***************************
     * Mapper
     **********************************/
    public String getMapperPath() {
        return CodeGenStringUtils.separator(codeGenEntity.mapperLocation() + "/" + getModelPrefix() + "Mapper.xml");
    }

    /***************************  Repository  ******************************/
    /**
     * Repository包名
     */
    public String getRepositoryPackage() {
        return relatedPath(getEntityPackage(), ".repository");
    }

    /**
     * Repository类名（含包名）
     */
    public String getRepositoryClass() {
        return getClzSimpleName(getRepositoryPackage(), "Repository");
    }

    /**
     * Repository路径
     */
    public String getRepositoryPath() {
        return getJavaPath(getRepositoryClass());
    }

    /***************************
     * MetaData
     ******************************/
    public List<MetaData> getEntityMetaList() {
        return entityMetaList;
    }

    /**
     * 剔除掉父类中已经声明的属性
     */
    public List<MetaData> getExcludeSuperMetaList() {
        List<MetaData> result = new ArrayList<>();
        if (CollectionUtils.isEmpty(entityMetaList)) {
            return result;
        }
        Set<String> superFields = ReflectUtils.getSuperFields(clz);
        if (CollectionUtils.isEmpty(superFields)) {
            return entityMetaList;
        }
        for (MetaData meta : entityMetaList) {
            if (!superFields.contains(meta.getJavaName())) {
                result.add(meta);
            }
        }
        return result;
    }

    /**
     * 导入Set
     */
    public Set<String> getImports() {
        Set<String> importSets = new HashSet<>();
        if (entityMetaList == null || entityMetaList.isEmpty()) {
            return importSets;
        }
        for (MetaData metaData : entityMetaList) {
            String clzName = metaData.getJavaTypeClass().getName();
            if (!clzName.startsWith("java.lang")) {
                importSets.add(clzName);
            }
        }
        return importSets;
    }

    /**
     * 导入Set
     */
    public Set<String> getExcludeSuperImports() {
        Set<String> importSets = new HashSet<>();
        List<MetaData> tempList = getExcludeSuperMetaList();
        if (tempList == null || tempList.isEmpty()) {
            return importSets;
        }
        for (MetaData metaData : tempList) {
            String clzName = metaData.getJavaTypeClass().getName();
            if (!clzName.startsWith("java.lang")) {
                importSets.add(clzName);
            }
        }
        return importSets;
    }

    /**
     * Model对象的导入库
     *
     * @return Set<String>
     */
    public Set<String> getModelImports() {
        Set<String> importSets = new HashSet<>();
        importSets.addAll(getImports());
        importSets.remove(Date.class.getName());
        return importSets;
    }

    /**
     * 校验注解与Entity名称一致性
     *
     * @throws BusinessException
     */
    public void validate() throws BusinessException {
        String simpleName = clz.getSimpleName();
        String settingName = codeGenEntity.prefix() + codeGenEntity.suffix();
        if (!StringUtils.equals(simpleName, settingName)) {
            throw new BusinessException(clz.getName() + "====>名称与注解不一致");
        }
        matchSuperClz();
    }

    /**
     * 校验Codegen Entity父类
     *
     * @throws BusinessException
     */
    private void matchSuperClz() throws BusinessException {
        String superClzName = clz.getSuperclass().getName();
        String reqClzName = codeGenEntity.jpaExtendsEntity();
        String prefix = "JPA CodeGen Entity";
        if (SystemContext.isORMMyBatis()) {
            prefix = "MyBatis CodeGen Entity";
            reqClzName = codeGenEntity.mybatisExtendsEntity();
        }
        if (!CodeGenStringUtils.equals(reqClzName, superClzName)) {
            throw new BusinessException(prefix + "必须继承[" + reqClzName + "]");
        }
    }

    private void initLoader() throws BusinessException {
        entityMetaList = new ArrayList<>();
        List<MetaData> dataList = initMetaData();
        if (dataList != null && !dataList.isEmpty()) {
            entityMetaList.addAll(dataList);
        }
    }

    private String relatedPath(String path, String dest) {
        String tempValue = path.replace(".model.entity", dest).replace(".entity", dest);
        if (tempValue.endsWith(".")) {
            tempValue = tempValue.substring(0, tempValue.length() - 1);
        }
        return tempValue;
    }

    private MetaLoader getMetaLoader() {
        DatabasePlatform platform = SystemContext.getCodeGenConfig().platform();
        if (DatabasePlatform.isMSQL(platform)) {
            return new MysqlLoader();
        } else {
            throw new RuntimeException("不支持的数据库类型");
        }
    }

    /**
     * 拼装类名
     */
    private String getClzSimpleName(String packageName, String suffix) {
        return packageName + "." + getModelPrefix() + suffix;
    }

    /**
     * 拼装包名和后缀,最后转换成路径
     */
    private String getJavaPath(String clzPackageName) {
        return CodeGenStringUtils.package2Path(clzPackageName) + ".java";
    }

    /**
     * 获取自定义属性列表
     */
    private List<MetaData> initMetaData() throws BusinessException {
        if (codeGenEntity.readDataBase()) {
            return getMetaLoader().getMetaData(codeGenEntity.table());
        }
        List<MetaData> metaDataList = new ArrayList<>();
        if (clz == null || !clz.isAnnotationPresent(CodeGenEntity.class)) {
            return metaDataList;
        }

        Field[] fields = clz.getDeclaredFields();
        if (fields == null || fields.length == 0) {
            return metaDataList;
        }
        for (Field field : fields) {
            boolean isNeed = field.isAnnotationPresent(CodeGenField.class);
            if (!isNeed) {
                continue;
            }
            CodeGenField fieldAnno = field.getAnnotation(CodeGenField.class);
            MetaData metaData = new MetaData();
            metaData.setJavaName(field.getName());
            metaData.setJavaTypeClass(field.getType());
            metaData.setColumnName(fieldAnno.column());
            metaData.setComment(fieldAnno.desc());
            metaDataList.add(metaData);
        }
        return metaDataList;
    }
}
